package transport

import (
	"errors"
	"fmt"
	"io"
	"net/url"
)

type ChannelIDFactory func() int64

type TransportPool interface {
	Add(transport Transport) error
	Remove(transport Transport) error
	WaitMessage() ([]Transport, []Transport, error)
	Close() error
	Init() error
}

// Transport defines a transport, transport is a connection between server and client.
// It's an abstract of transport level.
type Transport interface {
	io.ReadWriteCloser

	//FD returns the system file describer, used to register message queue
	FD() int

	// ID returns the id of the transport. This id can use as identifier of the transport.
	// The id is unique in the cluster.
	ID() int64

	// Ready notify that there's data can be read by system call. This is a suggestion func, transport can implement an empty method
	Ready()
}

// Acceptor defines transport acceptor
type Acceptor interface {
	Accept() (Transport, error)
	Close() error
}

// Factory defines transport factory
type Factory interface {

	// Schemes supported schemes.
	Schemes() Schemes

	// Listen for an address and accept the connection request.
	Listen(address *url.URL) (Acceptor, error)
}

// Schemes to define scheme list
type Schemes []string

// FixURL to fix scheme
func (ss Schemes) FixURL(u *url.URL) error {
	switch {
	case "" == u.Scheme:
		u.Scheme = ss[0]
	case !ss.Valid(u.Scheme):
		return fmt.Errorf("invalid scheme, %s, available: %v", u.Scheme, ss)
	}
	return nil
}

// Valid scheme
func (ss Schemes) Valid(scheme string) bool {
	return -1 != ss.indexOf(scheme)
}

// find scheme
func (ss Schemes) indexOf(scheme string) int {
	for index, s := range ss {
		if s == scheme {
			return index
		}
	}
	return -1
}

type Factories []Factory

func (factories Factories) Listen(addr *url.URL) (Acceptor, error) {
	scheme := addr.Scheme
	for _, f := range factories {
		for _, s := range f.Schemes() {
			if s == scheme {
				return f.Listen(addr)
			}
		}
	}
	return nil, errors.New(fmt.Sprintf("not support scheme: %s, addr: %s", scheme, addr.String()))
}
